home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include "global.h"
- #include "timer.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "internet.h"
- #include "tcp.h"
- #include "ip.h"
-
- static int16 hash_tcb __ARGS((struct connection *conn));
-
- /* TCP connection states */
- char *Tcpstates[] = {
- "Closed",
- "Listen",
- "SYN sent",
- "SYN received",
- "Established",
- "FIN wait 1",
- "FIN wait 2",
- "Close wait",
- "Closing",
- "Last ACK",
- "Time wait"
- };
-
- /* TCP closing reasons */
- char *Tcpreasons[] = {
- "Normal",
- "Reset/Refused",
- "Timeout",
- "ICMP"
- };
- struct tcb *Tcbs[NTCB];
- int16 Tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
- int32 Tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
- int Tcp_trace; /* State change tracing flag */
- struct tcp_rtt Tcp_rtt[RTTCACHE];
-
- /* Lookup connection, return TCB pointer or NULLTCB if nonexistant */
- struct tcb *
- lookup_tcb(conn)
- struct connection *conn;
- {
- register struct tcb *tcb;
-
- tcb = Tcbs[hash_tcb(conn)];
- while(tcb != NULLTCB){
- /* Yet another structure compatibility hack */
- if(conn->local.address == tcb->conn.local.address
- && conn->remote.address == tcb->conn.remote.address
- && conn->local.port == tcb->conn.local.port
- && conn->remote.port == tcb->conn.remote.port)
- break;
- tcb = tcb->next;
- }
- return tcb;
- }
-
- /* Create a TCB, return pointer. Return pointer if TCB already exists. */
- struct tcb *
- create_tcb(conn)
- struct connection *conn;
- {
- register struct tcb *tcb;
- struct tcp_rtt *tp;
-
- if((tcb = lookup_tcb(conn)) != NULLTCB)
- return tcb;
- if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB)
- return NULLTCB;
- ASSIGN(tcb->conn,*conn);
-
- tcb->cwind = tcb->mss = Tcp_mss;
- tcb->ssthresh = 65535;
- if((tp = rtt_get(tcb->conn.remote.address)) != NULLRTT){
- tcb->srtt = tp->srtt;
- tcb->mdev = tp->mdev;
- } else {
- tcb->srtt = Tcp_irtt; /* mdev = 0 */
- }
- /* Initialize timer intervals */
- tcb->timer.start = tcb->srtt / MSPTICK;
- tcb->timer.func = tcp_timeout;
- tcb->timer.arg = tcb;
-
- link_tcb(tcb);
- return tcb;
- }
-
- /* Close our TCB */
- void
- close_self(tcb,reason)
- register struct tcb *tcb;
- int reason;
- {
- struct reseq *rp1;
- register struct reseq *rp;
-
- if(tcb == NULLTCB)
- return;
-
- stop_timer(&tcb->timer);
- tcb->reason = reason;
-
- /* Flush reassembly queue; nothing more can arrive */
- for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
- rp1 = rp->next;
- free_p(rp->bp);
- free((char *)rp);
- }
- tcb->reseq = NULLRESEQ;
- setstate(tcb,CLOSED);
- }
-
- /* Sequence number comparisons
- * Return true if x is between low and high inclusive,
- * false otherwise
- */
- int
- seq_within(x,low,high)
- register int32 x,low,high;
- {
- if(low <= high){
- if(low <= x && x <= high)
- return 1;
- } else {
- if(low >= x && x >= high)
- return 1;
- }
- return 0;
- }
- int
- seq_lt(x,y)
- register int32 x,y;
- {
- return (long)(x-y) < 0;
- }
- #ifdef notdef
- int
- seq_le(x,y)
- register int32 x,y;
- {
- return (long)(x-y) <= 0;
- }
- #endif /* notdef */
- int
- seq_gt(x,y)
- register int32 x,y;
- {
- return (long)(x-y) > 0;
- }
- int
- seq_ge(x,y)
- register int32 x,y;
- {
- return (long)(x-y) >= 0;
- }
-
- /* Hash a connect structure into the hash chain header array */
- static int16
- hash_tcb(conn)
- struct connection *conn;
- {
- register unsigned int hval;
-
- /* Compute hash function on connection structure */
- hval = hiword(conn->remote.address);
- hval ^= loword(conn->remote.address);
- #ifdef notdef /* Never changes, so not really needed */
- hval ^= hiword(conn->local.address);
- hval ^= loword(conn->local.address);
- #endif
- hval ^= conn->remote.port;
- hval ^= conn->local.port;
- return hval % NTCB;
- }
- /* Insert TCB at head of proper hash chain */
- void
- link_tcb(tcb)
- register struct tcb *tcb;
- {
- register struct tcb **tcbhead;
- char i_state;
-
- tcb->prev = NULLTCB;
- i_state = dirps();
- tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
- tcb->next = *tcbhead;
- if(tcb->next != NULLTCB)
- tcb->next->prev = tcb;
-
- *tcbhead = tcb;
- restore(i_state);
- }
- /* Remove TCB from whatever hash chain it may be on */
- void
- unlink_tcb(tcb)
- register struct tcb *tcb;
- {
- register struct tcb **tcbhead;
- char i_state;
-
- i_state = dirps();
- tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
- if(tcb->prev == NULLTCB)
- *tcbhead = tcb->next; /* We're the first one on the chain */
- else
- tcb->prev->next = tcb->next;
- if(tcb->next != NULLTCB)
- tcb->next->prev = tcb->prev;
- restore(i_state);
- }
- void
- setstate(tcb,newstate)
- register struct tcb *tcb;
- register int newstate;
- {
- register char oldstate;
-
- oldstate = tcb->state;
- tcb->state = newstate;
- if(Tcp_trace)
- printf("TCB %lx %s -> %s\n",ptol(tcb),
- Tcpstates[oldstate],Tcpstates[newstate]);
- if(tcb->s_upcall)
- (*tcb->s_upcall)(tcb,oldstate,newstate);
-
- switch(newstate){
- case ESTABLISHED:
- /* Notify the user that he can begin sending data */
- if(tcb->t_upcall)
- (*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
- break;
- }
- }
- /* Convert TCP header in host format into mbuf ready for transmission,
- * link in data (if any), and compute checksum
- */
- struct mbuf *
- htontcp(tcph,data,ph)
- register struct tcp *tcph;
- struct mbuf *data;
- struct pseudo_header *ph;
- {
- int16 hdrlen;
- struct mbuf *bp;
- register char *cp;
- int16 csum;
-
- hdrlen = (tcph->mss != 0) ? TCPLEN + MSS_LENGTH : TCPLEN;
-
- if((bp = pushdown(data,hdrlen)) == NULLBUF){
- free_p(data);
- return NULLBUF;
- }
- cp = bp->data;
- cp = put16(cp,tcph->source);
- cp = put16(cp,tcph->dest);
- cp = put32(cp,tcph->seq);
- cp = put32(cp,tcph->ack);
- *cp++ = hdrlen << 2; /* Offset field */
- *cp = 0;
- if(tcph->flags.urg)
- *cp |= 32;
- if(tcph->flags.ack)
- *cp |= 16;
- if(tcph->flags.psh)
- *cp |= 8;
- if(tcph->flags.rst)
- *cp |= 4;
- if(tcph->flags.syn)
- *cp |= 2;
- if(tcph->flags.fin)
- *cp |= 1;
- cp++;
- cp = put16(cp,tcph->wnd);
- *cp++ = 0; /* Zero out checksum field */
- *cp++ = 0;
- cp = put16(cp,tcph->up);
-
- if(tcph->mss != 0){
- *cp++ = MSS_KIND;
- *cp++ = MSS_LENGTH;
- cp = put16(cp,tcph->mss);
- }
- csum = cksum(ph,bp,ph->length);
- /* Fill checksum field */
- put16(&bp->data[16],csum);
-
- return bp;
- }
- /* Pull TCP header off mbuf */
- int
- ntohtcp(tcph,bpp)
- register struct tcp *tcph;
- struct mbuf **bpp;
- {
- int16 hdrlen;
- int16 i,optlen;
- register int flags;
- char hdrbuf[TCPLEN];
-
- i = pullup(bpp,hdrbuf,TCPLEN);
- /* Note that the results will be garbage if the header is too short.
- * We don't check for this because returned ICMP messages will be
- * truncated, and we at least want to get the port numbers.
- */
- tcph->source = get16(&hdrbuf[0]);
- tcph->dest = get16(&hdrbuf[2]);
- tcph->seq = get32(&hdrbuf[4]);
- tcph->ack = get32(&hdrbuf[8]);
- hdrlen = (hdrbuf[12] & 0xf0) >> 2;
- flags = hdrbuf[13];
- tcph->flags.urg = flags & 32;
- tcph->flags.ack = flags & 16;
- tcph->flags.psh = flags & 8;
- tcph->flags.rst = flags & 4;
- tcph->flags.syn = flags & 2;
- tcph->flags.fin = flags & 1;
- tcph->wnd = get16(&hdrbuf[14]);
- tcph->up = get16(&hdrbuf[18]);
- tcph->mss = 0;
-
- /* Check for option field. Only space for one is allowed, but
- * since there's only one TCP option (MSS) this isn't a problem
- */
- if(i < TCPLEN || hdrlen < TCPLEN)
- return -1; /* Header smaller than legal minimum */
- if(hdrlen == TCPLEN)
- return hdrlen; /* No options, all done */
-
- if(hdrlen > len_mbuf(*bpp) + TCPLEN){
- /* Remainder too short for options length specified */
- return -1;
- }
- /* Process options */
- for(i=TCPLEN; i < hdrlen;){
- switch(pullchar(bpp)){
- case EOL_KIND:
- i++;
- goto eol; /* End of options list */
- case NOOP_KIND:
- i++;
- break;
- case MSS_KIND:
- optlen = pullchar(bpp);
- if(optlen == MSS_LENGTH)
- tcph->mss = pull16(bpp);
- i += optlen;
- break;
- }
- }
- eol:
- /* Get rid of any padding */
- if(i < hdrlen)
- pullup(bpp,NULLCHAR,hdrlen - i);
- return hdrlen;
- }
- /* Round trip timing cache routines.
- * These functions implement a very simple system for keeping track of
- * network performance for future use in new connections.
- * The emphasis here is on speed of update (rather than optimum cache hit
- * ratio) since rtt_add is called every time a TCP connection updates
- * its round trip estimate.
- */
- void
- rtt_add(addr,rtt)
- int32 addr; /* Destination IP address */
- int32 rtt;
- {
- register struct tcp_rtt *tp;
- int32 abserr;
-
- if(addr == 0)
- return;
- tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
- if(tp->addr != addr){
- /* New entry */
- tp->addr = addr;
- tp->srtt = rtt;
- tp->mdev = 0;
- } else {
- /* Run our own SRTT and MDEV integrators, with rounding */
- abserr = (rtt > tp->srtt) ? rtt - tp->srtt : tp->srtt - rtt;
- tp->srtt = ((AGAIN-1)*tp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
- tp->mdev = ((DGAIN-1)*tp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
- }
- }
- struct tcp_rtt *
- rtt_get(addr)
- int32 addr;
- {
- register struct tcp_rtt *tp;
-
- if(addr == 0)
- return NULLRTT;
- tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
- if(tp->addr != addr)
- return NULLRTT;
- return tp;
- }
-